/**
 * Copyright 2009 Henning Teek, Ingo Renner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.chigs.web.common.modal;

import java.io.Serializable;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.synyx.hades.domain.AbstractPersistable;

import com.googlecode.chigs.web.common.table.AjaxFallbackDefaultDataTableBuilder;


/**
 * 
 * @author Ingo Renner
 * @author Henning Teek
 *
 */
public abstract class AModalManagement<T extends AbstractPersistable<Long>> implements Serializable {
	private final static Logger log = LoggerFactory.getLogger(AModalManagement.class);
	private final String presenterId;
	private final String editorId;
	private final String createId;

	private T currentObject;
	private boolean editMode = false;
	private APresenterPanel<T> presenter;
	private AModalWindow<T> editor;
	private boolean delete = true;
	
	public AModalManagement(String presenterId, String editorId, String createId) {
		super();
		this.presenterId = presenterId;
		this.editorId = editorId;
		this.createId = createId;
	}

	protected abstract List<T> getValues();
	
	/**
	 * The presentation of T linked to an editor for T to create and edit objects of type T
	 * @return Component with presenterId
	 */
	public Component getPresenter() {
		if (presenter == null){
			presenter = new APresenterPanel<T> (presenterId, AModalManagement.this) {
				@Override
				protected void addColumns(AjaxFallbackDefaultDataTableBuilder builder) {
					AModalManagement.this.addColumns(builder);					
				}

				@Override
				protected void prepareRemove(AjaxRequestTarget target, T object) {
					AModalManagement.this.prepareRemove(target, object);
				}

				@Override
				protected void removeDone(AjaxRequestTarget target, T object) {
					AModalManagement.this.removeDone(target, object);
				}

				@Override
				protected void prepareAdd(AjaxRequestTarget target) {
					AModalManagement.this.prepareAdd(target);
				}
			};
		}
		presenter.modelChanged();
		return presenter;
	}
	
	protected void prepareRemove(AjaxRequestTarget target, T object) {
	}

	protected void removeDone(AjaxRequestTarget target, T object) {
	}

	protected void prepareAdd(AjaxRequestTarget target) {
	}
	
	@SuppressWarnings("unchecked")
	public Component getAddLink() {
		return ((APresenterPanel<T>)getPresenter()).getAddLink(getCreateId()).setVisible(isEditMode());
	}
	
	protected abstract void addColumns(AjaxFallbackDefaultDataTableBuilder builder);
	
	public AModalWindow<T> getNewEditor() {
		editor = null;
		return getEditor();
	}
	
	/**
	 * An editor of for object of type T
	 * @return Component with editorId
	 */
	public AModalWindow<T> getEditor() {
		if (editor == null){
			editor = createEditor(editorId, new PropertyModel<T>(AModalManagement.this,"currentObject"));
		}
		editor.modelChanged();
		return editor;
	}

	protected AModalWindow<T> createEditor(String id, PropertyModel<T> propertyModel) {
		return new AModalWindow<T>(id, propertyModel){
			@Override
			protected void afterCanel(AjaxRequestTarget target) {
				// reset current object
				getFreshCurrentObject();
				// create new Editor
				final AModalWindow<T> ce = getNewEditor();
				getEditorParent().addOrReplace(ce);
				target.addComponent(ce);
				// close current editor
				close(target);
			}

			@Override
			protected void afterSubmit(AjaxRequestTarget target, IModel<T> model) {
				if (log.isDebugEnabled()) {
					log.debug("afterSubmit, start modelObject=" + model.getObject().toString() + " target=" + target.toString());
					log.debug(getValues().size() + " values vor submit");
					for (T t : getValues()) {
						log.debug(t.toString());
					}
				}
				if (!isEditMode()) {
					// Adding an new object into our list
					notifyValueToAdd(getCurrentObject());
					if (log.isDebugEnabled()) {
						log.debug("afterSubmit, added modelObject=\n" + 
								model.getObject().toString() + "getCurrentObject()=\n" + 
								getCurrentObject().toString());
					}
				} else {
					// an existing object was changed. We remove the id, so a new entry will be created in db
					notifyValueChanged(getCurrentObject());
					if (log.isDebugEnabled()) {
						log.debug("afterSubmit, changed modelObject=\n" + 
								model.getObject().toString() + "getCurrentObject()=\n" + 
								getCurrentObject().toString());
					}
				}
				if (log.isDebugEnabled()) {
					log.debug(getValues().size() + " values after submit");
					for (T t : getValues()) {
						log.debug(t.toString());
					}
				}
				// reset current object
				getFreshCurrentObject();
				// display changes
				final Component cp = getPresenter();
				getPresenterParent().addOrReplace(cp);
				target.addComponent(cp);
				// create new Editor
				final AModalWindow<T> ce = getNewEditor();
				getEditorParent().addOrReplace(ce);
				target.addComponent(ce);
				// close current editor
				close(target);
			}

			@Override
			protected Component fillForm(String componentId, IModel<T> model) {
				return AModalManagement.this.fillForm(componentId, model);
			}
		};
	}

	/**
	 * override if you need more
	 * @param currentObject 
	 */
	protected void notifyValueToAdd(T currentObject) {
		getValues().add(getCurrentObject());
	}

	/**
	 * override if you need more
	 * @param currentObject 
	 */
	protected void notifyValueChanged(T currentObject) {
		getCurrentObject().setId(null);
	}

	/**
	 * @return The container where the wicket:id for the modal window is found.
	 */
	protected abstract MarkupContainer getEditorParent();

	/**
	 * @return The container where the wicket:id for the data presentation and create link is found.
	 */
	protected abstract MarkupContainer getPresenterParent();
	
	/**
	 * @param componentId Use this id for the Component to create 
	 * @param model with current object
	 * @return Component to be placed into the form of the modal window
	 */
	protected abstract Component fillForm(String componentId, IModel<T> model);

	public T getCurrentObject() {
		return currentObject;
	}

	public void setCurrentObject(T currentObject) {
		this.currentObject = currentObject;
	}

	/**
	 * @return Ein neues Object vom Typ T
	 */
	public abstract T getNewObject();
	
	/**
	 * Set current object to a new instance and return it.
	 * @return the current object
	 */
	public T getFreshCurrentObject(){
		currentObject = getNewObject();
		return currentObject;		
	}
	
	/**
	 * true if an existing object is the currentobject
	 * false if a new object is the current object
	 * @return true/false
	 */
	public boolean isEditMode() {
		return editMode;
	}

	public void setEditMode(boolean editMode) {
		this.editMode = editMode;
	}

	protected boolean isDelete() {
		return delete ;
	}

	public void setDelete(boolean delete) {
		this.delete = delete;
	}
	
	public String getPresenterId() {
		return presenterId;
	}

	public String getEditorId() {
		return editorId;
	}

	public String getCreateId() {
		return createId;
	}

}
